# Virtual Router Simulator 
#
# Copyright (C) 2009-2010 VRSim Project
# Author: Choonho Son <choonho@kt.com>
# URL: <http://vrsim.googlecode.com>
# For license information, see LICENSE.TXT
#

#import daemon

import libvirt
import subprocess as sub
import SocketServer
import signal
from xml.dom import minidom
#from VRSim.Node.XML import XML

import sys
from optparse import OptionParser
# Logging facility
import logging
import logging.handlers

SERVER = None

LOGGING = {'debug': logging.DEBUG,
          'info': logging.INFO,
          'warning': logging.WARNING,
          'error': logging.ERROR,
          'critical': logging.CRITICAL}

LOG_FILENAME = "/usr/local/var/log/vrsim/proxy_libvirtd_%s.out"

my_logger = logging.getLogger("Proxy_libvirtd")

usage = """

Proxy_libvirtd is Proxy for calling python libvirt module
usage :
# python Proxy_libvirtd.py -H <Server IP> -P <Port Number>
Server IP must be Public IP of Server, not 127.0.0.1
Default Port Number is 8000

The log file of VMServer is stored in /usr/local/var/log/vrsim/proxy_libvirtd.out
You can change log file by -L <logoption> option
- logoption: (debug|info|warning|error|critical)

"""

"""
domList = { 'Client IP': { dom_name: dom_instance, }   }
"""
domList = {}
conn = None
URI = "qemu:///system"


def connect(uri):
    try:
        conn = libvirt.open(uri)
        return conn
    except:
        msg = "Python error: fail to open libvirt module"
        print msg
        return False
        
            
class Proxy_libvirtd(SocketServer.BaseRequestHandler):
    def __init__(self,  host,  port,  id="Unknown"):
        my_logger = logging.getLogger("Proxy_libvirtd")
        my_logger.setLevel(logging.INFO)
        formatter = logging.Formatter("%(asctime)s - - %(levelname)s - \n%(message)s")
        # Add the log message handler to the logger
        logfile = LOG_FILENAME % id
        handler = logging.handlers.RotatingFileHandler(logfile, maxBytes=2000000, backupCount=5)
        handler.setFormatter(formatter)
        my_logger.addHandler(handler)

        # Signal
        #signal.signal(signal.SIGINT, self._handle_shutdown_signal)
        #signal.signal(signal.SIGTERM, self._handle_shutdown_signal)

        # self
        global SERVER
        SERVER = self
        
        self.uri = "qemu:///system"
        self.host = host
        self.port = port
        self.conn = connect(self.uri)
        self.server = SocketServer.TCPServer( (self.host,  self.port), ProxyTCPHandler )
        self.server.serve_forever()
    
    #def _handle_shutdown_signal(self,  *_):
    #    msg = "Signal received"
    #    my_logger.info(msg)
    #    self.server.shutdown()
    #    self.server.close()


class ProxyTCPHandler(SocketServer.BaseRequestHandler):
    """
    The RequestHandler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """
    
        
    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(10000).strip()
        self.client_ip = self.client_address[0]
        # DEBUG
        msg = "From requested IP (%s), data is:\n%s" % (self.client_address[0],  self.data)
        my_logger.debug("%s",  msg)
        
        node = minidom.parseString(self.data)
        result = self.parseOperation(node)
        
        # DEBUG
        msg = self.show()
        my_logger.debug("%s",  msg)
        
        # return the result
        my_logger.debug("Return value: %s",  result)
        self.request.send(result)
    
    def parseOperation(self,  dom):
        """ parse operation from client
        <proxy_libvirt>
        <opcode name='method'/>
        <id name='client node id'/>
        <content>
        ....
        </content>
        </proxy_libvirt>
        """
        node_opcode = dom.getElementsByTagName("opcode")[0]
        opcode_method = node_opcode.getAttribute("name")
        node_domain = dom.getElementsByTagName("domain")[0]
        
        msg = "opcode: %s\ndomain\n%s\n" % (opcode_method,  node_domain.toxml())
        #print "opcode:",  opcode_method
        #print "domain:\n" ,  node_domain.toxml()
        my_logger.info(msg)
        # parse method
        if opcode_method == "defineXML":
            # define domain
            xml = node_domain.toxml()
            msg = "proxy_libvirtd API: defineXML\n%s" % xml
            my_logger.debug(msg)
            domainInstance = self.defineXML(xml)
            instName = self.findNodeName(dom)
            myDomList = self.getDomByName(self.client_ip)
            myDomList[instName] = [domainInstance]
            return "True"
            
        elif opcode_method == "create":
            # create domain
            msg = "proxy_libvirtd API: create"
            my_logger.debug(msg)
            node_domain = dom.getElementsByTagName("domain")[0]
            dom_name = node_domain.getAttribute("name")
            myDomList = self.getDomByName(self.client_ip)
            #try:
            domInfo = myDomList[dom_name]
            domInfo[0].create()
            pid = self.getPID(dom_name)
            msg = "new virtual machine PID: %s" % pid
            my_logger.debug(msg)
            domInfo.append(pid)
            return "True"
            #except:
            #    debug("Fail ** Proxy_libvritd: create")
            #    return "False"
            
        elif opcode_method == "suspend":
            # suspend domain
            msg = "proxy_libvirtd API: suspend"
            my_logger.debug(msg)
            node_uuid = dom.getElementsByTagName("domain")[0]
            uuid = node_uuid.getAttribute("name")
            myDomList = self.getDomByName(self.client_ip)
            try:
                domInfo = myDomList[uuid]
                domInfo[0].suspend()
                return "True"
            except:
                return "False"
            
        elif opcode_method == "stop":
            # stop domain
            msg = "proxy_libvirtd API: stop"
            my_logger.debug(msg)
            node_name = dom.getElementsByTagName("domain")[0]
            name = node_name.getAttribute("name")
            myDomList = self.getDomByName(self.client_ip)
            #try:
            domInfo = myDomList[name]
            pid = domInfo[1]
            msg = "PID for kill: %s" % pid
            self.killProcess(pid)
            # delete information from DomList of client IP
            del myDomList[name]
            return "True"
            #except:
            #    debug("Proxy_libvirtd: fail to kill process")
            #    return "False"
        
        elif opcode_method == "quit":
            # kill all process
            #<proxy_libvirt>
            #<opcode name='quit'/>
            #<domain/>
            #</proxy_libvirt>
            msg = "stop all process"
            my_logger.info(msg)
            my_logger.info(self.show())
            for client in domList:
                for inst in domList[client]:
                    dom_inst = domList[client]
                    list = dom_inst[inst]
                    # PID exist?
                    if len(list) == 2:
                        msg = "kill process(%s)" % list[1]
                        self.killProcess(int(list[1]))
                        my_logger.info(msg)
            my_logger.info(self.show())
            if SERVER != None:
                msg = 'shutdown server socket'
                my_logger.info(msg)
                #SERVER.server.shutdown()
            else:
                msg = "SERVER is None"
                my_logger.info(msg)
            return "Exit"
            
        else:
            # undefined method
            msg = "proxy_libvirtd API: undefined method call: %s" % opcode_method
            my_logger.error(msg)
            
    def defineXML(self,  xml):
        try:
            conn = libvirt.open(URI)
        except:
            msg = "failed to open libvirt"
            my_logger.error(msg)
            return False
        dom = conn.defineXML(xml)
        return dom
        
    def getPID(self,  name):
        """ return proess ID of virtual node
        pid file: /usr/local/var/run/libvirt/qemu/<name>.pid
        """
        uri = "/usr/local/var/run/libvirt/qemu/" + name + ".pid"
        fp = open(uri,  'r')
        try:
            pid = fp.read()
            msg = "getPID: %s" % pid
            my_logger.debug(msg)
            fp.close()
            return pid
        except:
            msg = "Cannot open pid file: %s" + uri
            my_logger.error(msg)
            return None
        
        
    def killProcess(self,  pid):
        """ kill process by pid
        """
        cmd = "kill -9 " + str(pid)
        sub.Popen(cmd,  shell=True,  close_fds=True)
        return True
        
    def findNodeName(self,  xml):
        node_name = xml.getElementsByTagName("name")[0]
        name = node_name.firstChild.data
        return name
        
    def getDomByName(self,  name):
        key = name
        if not domList.has_key(key):
            domList[key] = {}
        
        list = domList[key]
        return list
     
    def show(self):
        message =  "===================================================================\n"
        message += "Client IP\tNode Name\tPID \n"
        message += "-------------------------------------------------------------------\n"
        for client in domList:
            message += client + "\n"
            for inst in domList[client]:
                dom_inst = domList[client]
                message += "\t\t" + inst
                list = dom_inst[inst]
                # PID exist?
                if len(list) == 2:
                    message += "\t" + list[1]
                message += "\n"
        return message
        

#def run():
if __name__ == "__main__":
    # OptionParser
    parser = OptionParser()
    # Host
    parser.add_option("-H", "--host", dest="host", help="specify VMServer IP")
    # Port
    parser.add_option("-P",  "--port",  dest="port",  help="specify VMServer Port")
    # User
    parser.add_option("-F",  "--file",  dest="file",  help="specify log file name")
    # Log
    parser.add_option("-L",  "--log",  dest="log",  help="specify Logging level (debug|info|warning|error|critical)")
    
    (options, args) = parser.parse_args()
    # host
    host = ""
    if options.host:
        host = options.host
    # port
    port = 8001
    if options.port:
        port = int(options.port)
    # logfile
    filename = "undefined"
    if options.file:
        filename = options.file
        
    
    myinst = Proxy_libvirtd(host,  port,  filename)
    
    # update log option
    if options.log:
        logging_level = LOGGING[options.log]
    else:
        logging_level = LOGGING["debug"]
    my_logger.setLevel(logging_level)   
    
    
#if __name__ == "__main__":
#    with daemon.DaemonContext():
#        run()
